﻿using Microsoft.Extensions.Logging;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;

namespace filesync
{
    public class FileSync
    {
        private bool stop = false;

        private bool fileChange = false;

        private Process cmdProcess = null;

        private readonly FileSyncOptions options;
        private FileSystemWatcher fileSystemWatcher;
        private ILogger logger;
        public FileSync(FileSyncOptions options, ILogger logger)
        {
            this.logger = logger;
            this.options = options;
        }

        public void Start()
        {
            if (!EnsurePath())
            {
                return;
            }

            logger.LogInformation($"Start watch file {options.Path}");

            cmdProcess = new Process
            {
                StartInfo = new ProcessStartInfo("cmd.exe")
                {
                    RedirectStandardOutput = true,
                    RedirectStandardInput = true,
                    UseShellExecute = false,
                }
            };

            cmdProcess.Exited += (a, b) =>
            {
                cmdProcess.Start();
            };
            cmdProcess.Start();

            cmdProcess.StandardInput.WriteLine($"cd /d {options.Path}");
            fileSystemWatcher = new FileSystemWatcher
            {
                Path = options.Path,
            };
            fileSystemWatcher.Changed += OnFileChange;
            fileSystemWatcher.Created += OnFileChange;
            fileSystemWatcher.Deleted += OnFileChange;
            fileSystemWatcher.Renamed += OnFileChange;
            fileSystemWatcher.NotifyFilter = NotifyFilters.LastAccess
                            | NotifyFilters.LastWrite
                            | NotifyFilters.FileName
                            | NotifyFilters.DirectoryName;

            fileSystemWatcher.Filters.Add("*.*");
            fileSystemWatcher.IncludeSubdirectories = true;
            fileSystemWatcher.EnableRaisingEvents = true;

            var timer = new Timer(obj =>
            {
                if (!fileChange) return;
                fileChange = false;
                Sync();
            }, null, 0, 5000);

            while (!stop)
            {
                fileSystemWatcher.WaitForChanged(WatcherChangeTypes.All);
            }

        }

        private void OnFileChange(object file, FileSystemEventArgs args)
        {
            if (args.FullPath.Contains(".git"))
            {
                return;
            }

            fileChange = true;
        }

        private void Sync()
        {
            FilterFiles();
            logger.LogInformation("start sync");
            cmdProcess.StandardInput.WriteLine("git add -A");

            cmdProcess.StandardInput.WriteLine("git commit -m \"sync change\"");
            cmdProcess.StandardInput.WriteLine("git push");
            cmdProcess.StandardInput.WriteLine("git pull");
            logger.LogInformation("sync success");
        }

        private void FilterFiles()
        {
            var files = Directory.EnumerateFiles(options.Path, "*", SearchOption.AllDirectories).Where(x =>
            {
                var file = new FileInfo(x);
                return file.Length > 50 * 1024 * 1024;
            }).Select(x => x.Replace(options.Path, "")).ToList();

            logger.LogInformation(String.Join(',', files));

            File.WriteAllLines($"{options.Path}\\.git\\info\\exclude", files);
        }

        private bool EnsurePath()
        {
            if (!Directory.Exists(options.Path))
            {
                logger.LogInformation("目录不存在");
                return false;
            }

            if (!Directory.Exists($"{options.Path}\\.git"))
            {
                logger.LogInformation("不是一个有效的git仓库");
                return false;
            }

            return true;
        }

        public void Stop()
        {
            try
            {
                stop = true;
                cmdProcess?.Kill();
                cmdProcess?.Dispose();
                fileSystemWatcher?.Dispose();
            }
            catch (Exception)
            {
            }
        }
    }
}
